/*
 * @(#)MemoListViewNew.java $version 2013. 4. 5.
 *
 * Copyright 2007 NHN Corp. All rights Reserved. 
 * NHN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.design.oop.control;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationSet;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import android.widget.AdapterView;
import android.widget.ListAdapter;

import com.design.oop.WheatBeerApplication;
import com.design.oop.util.CommonUtil;
import com.design.oop.util.DisplayUtil;

/**
 * @author ruin
 */
public class AnimateListView<T, I> extends AdapterView<ListAdapter> {
	static final boolean USE_ANIMATION = true;
	private static final int ANIMATION_DURATION = 300;
	private static final int ANIMATION_DELAY = 70;
	private static final int FILL_INDEX = 2131427560;
	private static final int OLD_RECT = 2131444112;
	private static final int START_ANIMATION = 2131444444;

	private int numColumns = 2;
	private float brickPadding = 6.67f;

	private int minHeight;
	private int widthSpec;
	private int columnWidth;
	private AbsAnimateListAdapter<T, I> adapter;
	private CustomScrollView scrollView;
	private List<Integer> columnHeightHistorys = new ArrayList<Integer>();
	private ArrayList<Integer> columnLastHeight = new ArrayList<Integer>(2);

	//	private HashMap<Integer, View> visibleChildList = new HashMap<Integer, View>();
	private Queue<View> recycleViewQueue = new LinkedList<View>();
	private SparseArray<View> reuseViewMap = new SparseArray<View>();
	private boolean dataChanged;

	//	private Rect displayRect;

	public AnimateListView(Context context, AttributeSet attributeSet) {
		super(context, attributeSet);

		// set minHeight
		int adHeight = DisplayUtil.getPixelFromDP(30);
		minHeight = DisplayUtil.getDisplayHeight() - (WheatBeerApplication.getStatusBarHeight() + adHeight);
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		makeBlueprint();
		int maxHeight = CommonUtil.isEmptyList(columnLastHeight) ? 0 : Collections.max(columnLastHeight);
		int heightSize = Math.max(maxHeight, minHeight);
		int widthSize = MeasureSpec.getSize(widthMeasureSpec) == 0 ? DisplayUtil.getDisplayWidth() : MeasureSpec.getSize(widthMeasureSpec);
		setMeasuredDimension(widthSize, heightSize);
	}

	Rect displayRect = new Rect();
	private List<Integer> mVisiblePositions = new ArrayList<Integer>();
	private List<Rect> mOldRects = new ArrayList<Rect>();;

	@Override
	protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
		displayRect.set(getLeft(), getScrolledTop(), getRight(), getScrolledBottom());
		initItems();

		fireStartAnimationEvent();

		super.onLayout(changed, left, top, right, bottom);
	}

	/**
	 * 뷰에 설정된 에니메이션을 모두 시작한다.
	 */
	private void fireStartAnimationEvent() {
		if (!USE_ANIMATION) { //에니메이션을 사용하지 않는다면.
			return;
		}
		clearDisappearingChildren();
		int childCount = getChildCount();
		for (int i = 0; i < childCount; i++) {
			View child = getChildAt(i);
			Boolean isStarted = (Boolean)child.getTag(START_ANIMATION); //뷰에서 에니메이션 플레그를 읽어서 구동 되었는지 인지 한다. (중복실행 방지)
			Animation animation = child.getAnimation();
			if (isStarted != null && !isStarted && animation != null) {
				CustomAnimationListener listener = new CustomAnimationListener(0, i);
				animation.setAnimationListener(listener);
				child.startAnimation(animation);
				child.setTag(START_ANIMATION, true);
			}
		}
	}

	/**
	 * 아이템이 셋팅되는 시점에 아이템 초기화
	 */
	synchronized private void initItems() {
		//		if (mOldRects == null) {
		//			mOldRects = new ArrayList<Rect>();
		//		}
		//		mOldRects = new ArrayList<Rect>(mItemRects);
		//		Collections.copy(mOldRects, mItemRects); //복사해 놓는다.

		//blueprint 생성
		makeBlueprint();

		//visiable Item 추출.
		mFirstVisiblePosition = calcFirstVisiblePosition();

		List<Integer> visiblePositions = collectVisiblePositions(mFirstVisiblePosition); //노출되는 item position을 조회
		SparseIntArray evictItems = collectEvictItem(visiblePositions); //invalid 하거나 invisible 한 item을 view에서 제거 한다.

		List<Integer> attachItems = collectAttachItems(mVisiblePositions, visiblePositions);
		detachEvictList(evictItems); //기존에 보여지던 뷰중에서 삭제대상이 되면 삭제한다.

		attachVisibleItem(attachItems, mIndexIncrement); //visible Item 을 view에 셋팅한다.

		mIndexIncrement = 0;

		mVisiblePositions = visiblePositions;

	}

	/**
	 * @param oldVisibleList
	 * @param visiblePositions
	 * @return
	 */
	private List<Integer> collectAttachItems(List<Integer> oldVisibleList, List<Integer> visiblePositions) {
		List<Integer> result = new ArrayList<Integer>();

		if (CommonUtil.isEmptyList(oldVisibleList)) { //기존에 그린게 하나도 없다면 전부 그릴대상
			return visiblePositions;
		}

		SparseIntArray index = new SparseIntArray();
		for (int position : oldVisibleList) {
			index.append(position, position);
		}

		for (int position : visiblePositions) { //기존에 보이던것중에 새로운 리스트에 포함 안된다면
			if (index.get(position, -1) == -1) { //기존에 없던거라면.
				result.add(position);
			}
		}
		return result;
	}

	private void detachEvictList(SparseIntArray evictItems) {
		int childCount = getChildCount();
		List<View> children = new ArrayList<View>();
		for (int i = 0; i < childCount; i++) {
			View child = getChildAt(i);
			int viewPosition = (Integer)child.getTag(FILL_INDEX);

			if (viewPosition == evictItems.get(viewPosition, -1)) { //삭제 대상에 포함되면.
				children.add(child);
			}
		}

		for (View child : children) {
			removeViewInLayout(child);
			recycleViewQueue.offer(child); //재활용 대상에 추가.
		}
	}

	/**
	 * 보여지는 position을 추출해낸다.
	 * 
	 * @param firstVisible
	 * @return
	 */
	private List<Integer> collectVisiblePositions(int firstVisible) {
		List<Integer> visiblePositions = new ArrayList<Integer>();
		Rect visibleRect = new Rect(displayRect);

		//		visibleRect.top -= visibleRect.height(); //총 3배의 공간을 로드해 놓는다.
		//		visibleRect.bottom += visibleRect.height();
		//		
		visibleRect.top -= visibleRect.height() / 2;
		visibleRect.bottom += visibleRect.height() / 2;

		int count = adapter.getCount();
		List<Rect> itemRects = mItemRects;

		//기존에 보이고 있는 애들은 제외한다. 그게 좋겠다.

		for (int i = firstVisible; i < count; i++) { //기본적으로 자식 카운트만큼 반복
			Rect itemRect = itemRects.get(i); //child bound
			if (Rect.intersects(itemRect, visibleRect)) { //충돌점이 있다면
				visiblePositions.add(i);
			}
		}
		return visiblePositions;
	}

	/**
	 * 과거의 노출 아이템과 새로우 노출할 아이템을 비교해서 
	 * 제거해야할 대상을 추출한다.
	 * 
	 * @param visiblePositions
	 * @return
	 */
	private SparseIntArray collectEvictItem(List<Integer> visiblePositions) { //노출되지 않는 자식뷰를 삭제해서 recycleQueue 에 넣는다.
		//이전에 노출되었던 뷰와 다른 점을 찾아서 visibleposition에 포함되지 않는 positon 을 가진 view를 부모에서 띄어낸다.
		List<Integer> oldPositions = mVisiblePositions;
		SparseIntArray evictChildPositionList = new SparseIntArray();

		if (CommonUtil.isEmptyList(oldPositions) || CommonUtil.isEmptyList(visiblePositions)) { //기존에 그려진 뷰가 없는거다.
			return evictChildPositionList;
		}
		SparseIntArray index = new SparseIntArray();
		for (int position : visiblePositions) {
			index.append(position, position);
		}
		for (int position : oldPositions) {
			if (index.get(position, -1) == -1) {
				evictChildPositionList.append(position, position);
			}
		}
		int childCount = getChildCount();
		for (int i = 0; i < childCount; i++) { //자식중에서도 안보이는 애들 제거
			View child = getChildAt(i);
			int position = (Integer)child.getTag(FILL_INDEX);
			if (index.get(position, -1) == -1) {
				evictChildPositionList.append(position, position);
			}
		}
		return evictChildPositionList;
	}

	/**
	 * 화면에 배치된 자식 뷰들
	 */
	//	SparseArray<View> mAllocatedChildView = new SparseArray<View>();

	/**
	 * 화면에 보여지는 item을 화면에 노출 시켜주는 역할을 한다.
	 * 이곳에서 에니메이션을 위한 마킹 작업을 수행한다.
	 * 
	 * @param visiblePositions 
	 * @param detatchViews 
	 */
	private void attachVisibleItem(List<Integer> visiblePositions, int indexIncrement) {
		int startTime = 0;
		for (int position : visiblePositions) { //화면에 보여지는 position들을 가지고 설정을 시작한다.
			int modedIndex = indexIncrement + position;
			Rect itemRect = mItemRects.get(position);

			View child = reuseViewMap.get(modedIndex);
			if (child == null) { //혹시 재사용 가능 한 뷰가 있다면 쓰고 없다면 셋팅!
				child = adapter.getView(position, recycleViewQueue.poll(), null);
			}

			LayoutParams localLayoutParams = (LayoutParams)CommonUtil.defaultIfNull(child.getLayoutParams(), new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

			child.measure(widthSpec, MeasureSpec.makeMeasureSpec(adapter.getItemHeight(position, child), MeasureSpec.AT_MOST));
			child.layout(itemRect.left, itemRect.top, itemRect.right, itemRect.bottom);
			child.setTag(FILL_INDEX, position);
			child.setTag(OLD_RECT, itemRect);

			Rect oldRect = null;

			if (CommonUtil.isNotEmptyList(mOldRects) && mOldRects.size() > modedIndex) {
				oldRect = mOldRects.get(Math.max(modedIndex, 0));
			} else { //만약 없다면 현재 계산된 좌표를 넣어준다.
				oldRect = mItemRects.get(position);
			}
			if (USE_ANIMATION) {
				if (oldRect != null) {
					int x = itemRect.left - oldRect.left;
					int y = itemRect.top - oldRect.top;

					if (modedIndex == -1) { //추가용 에니메이션
						Animation animation = getInsertAnimation(child, startTime + ANIMATION_DURATION);
						child.clearAnimation();
						child.setAnimation(animation);

					} else {
						Animation animation = getMoveAnimation(child, startTime, x, y);
						startTime += 80;
						child.clearAnimation();
						child.setAnimation(animation);
					}
					child.setTag(START_ANIMATION, false);
				} else {
					child.setAnimation(null);
				}
			}
			addViewInLayout(child, -1, localLayoutParams, true);
			//			child.startAnimation(animation);
			//			mAllocatedChildView.append(position, child);
		}
		reuseViewMap.clear();
	}

	/**
	 * 화면에 보여지는 첫번째 아이템의 포지션을 계산해 낸다.
	 * @return
	 */
	private int calcFirstVisiblePosition() {
		List<Rect> itemRects = mItemRects;
		int itemCount = adapter.getCount();
		Rect visibleRect = new Rect(displayRect);

		visibleRect.top -= visibleRect.height() / 2;
		visibleRect.bottom += visibleRect.height() / 2;

		for (int position = 0; position < itemCount; position++) { //기본적으로 자식 카운트만큼 반복
			Rect itemRect = itemRects.get(position); //child bound
			if (Rect.intersects(itemRect, visibleRect)) { //충돌점이 있다면
				return position;
			}
		}

		return 0;
	}

	/**
	 * 지정된 position 이후의 모든 정보를 무효화 시킨다.
	 * 
	 * @param position
	 */
	private void invalidateAfter(int position) {
		int childCount = getChildCount();
		//자식 뷰를 가져와서 자식 뷰가 무효한 아이템이라면 
		//부모에서 띄어낸후에 recycleQueue 에 넣어 보관한다.
		List<View> children = new ArrayList<View>();
		for (int i = 0; i < childCount; i++) { //바로 조회하며서 하면 삭제되는경우 자식수에 변경이 생기므로 오류 발생할수 있어 루프문 두번으로 처리
			children.add(getChildAt(i));
		}

		//		removeAllViewsInLayout();
		for (View child : children) { //부모뷰에서 자식 띄어내기!
			Integer viewPosition = (Integer)child.getTag(FILL_INDEX);
			if (viewPosition >= position) { //무효화 대상이므로
				removeViewInLayout(child);
				reuseViewMap.append(viewPosition, child);
				//				mAllocatedChildView.remove(position);
				//				recycleViewQueue.offer(child); //재활용 대상에 추가.
			}
		}

		((AbsAnimateListAdapter<T, I>)getAdapter()).resetCachedItemHeights();

		//그리기 영역 무효화
		invalidateRects(position);

		invalidateStartHeight(position);

		Iterator<Integer> iterator = mVisiblePositions.iterator();
		while (iterator.hasNext()) {
			int viewPosition = iterator.next();
			if (viewPosition >= position) {
				iterator.remove();
			}
		}

		//		clearDisappearingChildren();

	}

	/**
	 * @param position
	 */
	private void invalidateRects(int position) {
		mOldRects = new ArrayList<Rect>(mItemRects);
		mItemRects = new ArrayList<Rect>(mItemRects.subList(0, Math.min(position, mItemRects.size()))); //잘라서 넣기
	}

	/**
	 * @param view
	 * @see android.view.ViewGroup#removeViewInLayout(android.view.View)
	 */
	@Override
	public void removeViewInLayout(View view) {
		super.removeViewInLayout(view);
	}

	private void makeBlueprint() {
		makeBlueprint(mItemRects.size(), false);
	}

	private int getColumnIndex(int height) {
		int result = columnLastHeight.indexOf(height); //인덱스를 찾는것이니까.
		return result % 2;
	}

	private int getLastYPosition(int index) {
		if (columnLastHeight.size() == 0) {
			columnLastHeight.add(0);
			columnLastHeight.add(0);
		}
		return Math.min(columnLastHeight.get(0), columnLastHeight.get(1));
	}

	private void setHeightHistory(int columnIndex, int index, int posY) {
		if (index >= columnHeightHistorys.size()) { //높이 히스토리 기록용
			columnHeightHistorys.add(index, posY);
		} else {
			columnHeightHistorys.set(index, posY);
		}

		columnLastHeight.set(columnIndex, posY);
	}

	List<Rect> mItemRects = new ArrayList<Rect>();

	private Rect getItemRect(int index) {
		if (index >= mItemRects.size()) {
			return null;
		}
		return mItemRects.get(index);
	}

	/**
	 * 
	 */
	private void setItemRect(int index, Rect rect) {
		if (index >= mItemRects.size()) { //밖이라면 추가
			mItemRects.add(index, rect);
		} else {
			mItemRects.set(index, rect); //이미 있다면 switch 
		}
	}

	private void makeBlueprint(int startIndex, boolean markAnimation) {
		columnWidth = ((getMeasuredWidth() - DisplayUtil.getPixelFromDP(brickPadding)) / numColumns);
		WheatBeerApplication.setColumnWidth(columnWidth);
		widthSpec = MeasureSpec.makeMeasureSpec(columnWidth, MeasureSpec.EXACTLY);

		if (adapter == null) {
			return;
		}

		int count = adapter.getCount();

		for (int i = startIndex; i < count; i++) {
			Rect rect = getItemRect(i);
			if (rect != null) {
				continue;
			}
			int startY = getLastYPosition(i);
			int columnIndex = getColumnIndex(startY);

			int itemHeight = adapter.getItemHeight(i, null); //뷰에 직접 기록한다.

			int itemLeft = (columnWidth * columnIndex);
			if (columnIndex == 1) {
				itemLeft += DisplayUtil.getPixelFromDP(brickPadding);
			}
			int itemTop = startY;
			int itemRight = columnWidth + itemLeft;
			int itemBottom = itemTop + itemHeight;
			Rect itemRect = new Rect(itemLeft, itemTop, itemRight, itemBottom);

			setItemRect(i, itemRect);

			int itemNewColumnHeight = itemTop + itemHeight;

			setHeightHistory(columnIndex, i, itemNewColumnHeight);
		}
	}

	public void reset() {
		columnHeightHistorys = new ArrayList<Integer>();
		columnLastHeight.clear();
		for (int i = 0; i < numColumns; i++) {
			columnLastHeight.add(0);
		}

		List<View> removedChildren = new ArrayList<View>();
		int count = getChildCount();

		for (int i = 0; i < count; i++) {
			View child = getChildAt(i);
			removedChildren.add(child);
		}
		removeAllViewsInLayout(); //자식뷰 삭제

		recycleViewQueue.addAll(removedChildren); //recycle Queue 에 삭제된 뷰를 모두 넣는다.

		//		visibleChildList.clear();
		if (adapter != null) { //데이터 초기화 
			adapter.setDataSource(null);
		}
		//
		mItemRects.clear();
		mVisiblePositions.clear();
		requestLayout();
	}

	int mFirstVisiblePosition = 0;

	public void onScrollChanged() {
		requestLayout();
	}

	//--------------------------------------------------------------------------------
	// getters and setters
	//--------------------------------------------------------------------------------
	public int getScrolledTop() {
		return scrollView.getScrollY() - getTop();
	}

	public int getScrolledBottom() {
		return scrollView.getScrollY() + scrollView.getMeasuredHeight();
	}

	public void setContainingScrollView(CustomScrollView scrollView) {
		this.scrollView = scrollView;
	}

	public int getColumnWidth() {
		return columnWidth;
	}

	@Override
	public ListAdapter getAdapter() {
		return adapter;
	}

	@Override
	public void setAdapter(ListAdapter adapter) {
		if (this.adapter != null) {
			this.adapter.unregisterDataSetObserver(dataObserver);
		}
		this.adapter = (AbsAnimateListAdapter<T, I>)adapter;
		this.adapter.registerDataSetObserver(dataObserver);
		reset();
	}

	@Override
	public View getSelectedView() {
		return null;
	}

	@Override
	public void setSelection(int position) {
	}

	public void setNumColumns(int numColumns) {
		this.numColumns = numColumns;
	}

	private DataSetObserver dataObserver = new DataSetObserver() {
		public void onChanged() {
			synchronized (AnimateListView.this) {//데이터가 변경되었다고 인지 한다.
				dataChanged = true;
			}
			requestLayout();
		}

		public void onInvalidated() {
			//			reset();
			initItems();
			requestLayout();
		}
	};

	View mSelectedView;

	/**
	 * 아이템 터치여부를 인지 하기 위한 터치 이벤트를 구현한다.
	 * 
	 * @param ev
	 * @return
	 * @see android.view.ViewGroup#onInterceptTouchEvent(android.view.MotionEvent)
	 */
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
			case MotionEvent.ACTION_DOWN:
				mSelectedView = pointToView((int)ev.getX(), (int)ev.getY());
				sendLongTouchMessage(ev);
				break;
			case MotionEvent.ACTION_CANCEL:
			case MotionEvent.ACTION_UP:

				handler.removeMessages(MSG_LONG);
				break;
		}

		return false;
	}

	/**
	 * @param ev
	 */
	private void sendLongTouchMessage(MotionEvent ev) {
		Message msg = handler.obtainMessage();
		msg.what = MSG_LONG;
		msg.arg1 = (int)ev.getX();
		msg.arg2 = (int)ev.getY();
		handler.sendMessageDelayed(msg, 600);
	};

	public static final int MSG_LONG = 100;
	LongPressHandler handler = new LongPressHandler(); //하나만 있어도 된다. 클래스에 

	public class LongPressHandler extends Handler {
		/**
		 * @param msg
		 * @see android.os.Handler#handleMessage(android.os.Message)
		 */
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
				case MSG_LONG:
					//
					if (mSelectedView != null && getOnItemLongClickListener() != null) {
						OnItemLongClickListener listener = getOnItemLongClickListener();
						int x = msg.arg1;
						int y = msg.arg2;
						int position = pointToPosition(x, y);
						if (position == -1) {
							return;
						}
						long id = adapter.getItemId(position);
						listener.onItemLongClick(AnimateListView.this, mSelectedView, position, id);
						dispatchTouchEvent(MotionEvent.obtain(System.currentTimeMillis(), System.currentTimeMillis(), MotionEvent.ACTION_CANCEL, 0, 0, 0)); //캔슬 이벤트를 날려 이후 클릭 이벤트를 취소시킨다.
					}
					break;
			}
		}
	}

	@Override
	public int getPositionForView(View item) {
		final int childCount = getChildCount();
		for (int i = 0; i < childCount; i++) {
			if (getChildAt(i).equals(item)) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * @return
	 * @see android.widget.AdapterView#getFirstVisiblePosition()
	 */
	@Override
	public int getFirstVisiblePosition() {
		return mFirstVisiblePosition;
	}

	public View findChildByPosition(int position) {
		int count = getChildCount();
		for (int i = 0; i < count; i++) {
			View child = getChildAt(i);
			int index = (Integer)child.getTag(FILL_INDEX);
			if (index == position) {
				return child;
			}
		}
		return null;
	}

	public int pointToPosition(int x, int y) {
		//		Rect visibleRect = displayRect; //보여지는 영역
		//		int firstPosition = getFirstVisiblePosition();
		List<Rect> itemRects = mItemRects; //그려진 자식들.
		final int count = itemRects.size();
		//		y += visibleRect.top;
		for (int i = 0; i < count; i++) {
			//			final View child = getChildAt(i);
			Rect rect = itemRects.get(i);
			if (rect.contains(x, y)) {
				return i;
			}
		}
		return INVALID_POSITION;
	}

	public View pointToView(int x, int y) {
		//		int index = pointToPosition(x, y); //아이템 인덱스
		int count = getChildCount(); //현재 노출되는 자식들을 조회해 온다.
		Rect rect = new Rect();

		for (int i = 0; i < count; i++) { //첫번째 보여지는 뷰의 index를 가지고 있어야 한다.
			View child = getChildAt(i); //자식들을 조회해서
			child.getHitRect(rect);
			if (rect.contains(x, y)) {
				return child;
			}
		}
		return null;
	}

	public class CustomAnimationListener implements AnimationListener, Runnable {
		int position = 0;
		int command = 0;

		public static final int MOVE = 0;
		public static final int DELETE = 1;
		public static final int ADD = 2;

		public CustomAnimationListener() {
			command = DELETE;
		}

		public CustomAnimationListener(int command, int position) {
			this.position = position;
			this.command = command;
		}

		public int getPosition() {
			return position;
		}

		public void setPosition(int position) {
			this.position = position;
		}

		/**
		 * @param animation
		 * @see android.view.animation.Animation.AnimationListener#onAnimationStart(android.view.animation.Animation)
		 */
		@Override
		public void onAnimationStart(Animation animation) {
		}

		/**
		 * @param animation
		 * @see android.view.animation.Animation.AnimationListener#onAnimationEnd(android.view.animation.Animation)
		 */
		@Override
		public void onAnimationEnd(Animation animation) {
			post(this);

		}

		/**
		 * @param animation
		 * @see android.view.animation.Animation.AnimationListener#onAnimationRepeat(android.view.animation.Animation)
		 */
		@Override
		public void onAnimationRepeat(Animation animation) {
		}

		/**
		 * 
		 * @see java.lang.Runnable#run()
		 */
		@Override
		public void run() {
			switch (command) {
				case MOVE:
					if (mOldRects != null) {
						mOldRects.clear();
					}
					break;
				case ADD:
					break;
				case DELETE:
					remove(position);
					break;
			}
			clearDisappearingChildren();
		}

	}

	CustomAnimationListener animationListener = new CustomAnimationListener();
	private int mIndexIncrement;

	public void removeWithAnimation(int position) {
		View view = null;
		if (USE_ANIMATION) {
			int childCount = getChildCount();
			for (int i = 0; i < childCount; i++) {
				View child = getChildAt(i);
				int tagPostion = (Integer)child.getTag(FILL_INDEX);
				if (tagPostion == position) {
					view = child;
					break;
				}
			}

			if (view != null) {
				animationListener.setPosition(position);
				Animation animation = getRemoveAnimation(view, animationListener);
				view.clearAnimation();

				view.startAnimation(animation);
			}
		} else {
			remove(position);
		}
	}

	public void addItemWithAnimation(I item) {
		mIndexIncrement = -1;

		adapter.addItem(0, item);
		invalidateAfter(0); //전체를 무효화 시킨다.
		layout(getLeft(), getTop(), getRight(), getBottom() + 1);
	}

	/**
	 * @param position
	 */
	public void remove(int position) {
		adapter.remove(position);
		invalidateAfter(position);
		adapter.notifyDataSetChanged();
		//		requestLayout();
		mIndexIncrement = 1;
		layout(getLeft(), getTop(), getRight(), getBottom() + 1);
		invalidate();
	}

	/**
	 * @param position
	 */
	private void invalidateStartHeight(int position) {
		synchronized (columnHeightHistorys) {
			columnHeightHistorys = columnHeightHistorys.subList(0, position);
		}
		columnLastHeight.clear(); //초기화 하고
		columnLastHeight.add(0);
		columnLastHeight.add(0);
		int columnIndex = 0;
		int maxHeight = 0;
		for (int height : columnHeightHistorys) { //가장 큰거 두개 찾아내고 그걸 셋팅하면 되는데 어느쪽이 왼쪽인지... -_-?
			if (maxHeight <= height) {
				maxHeight = height;
				columnLastHeight.set(columnIndex, height);
				columnIndex = (columnIndex + 1) % 2; //토글
			} else {
				columnLastHeight.set(columnIndex, height);
			}
		}
	}

	public Animation getMoveAnimation(View view, long startTime, int dx, int dy) {
		AnimationSet animationSet = new AnimationSet(true);
		animationSet.setInterpolator(getContext(), android.R.anim.accelerate_interpolator);
		animationSet.setStartOffset(startTime);
		animationSet.setDuration(ANIMATION_DURATION);

		Animation animation = new TranslateAnimation(Animation.ABSOLUTE, -dx, Animation.ABSOLUTE, 0, Animation.ABSOLUTE, -dy, Animation.ABSOLUTE, 0);
		animation.setDuration(ANIMATION_DURATION);
		animationSet.addAnimation(animation);

		//		animation = new RotateAnimation(-5, 0, view.getWidth() / 2, 0);
		//
		//		animationSet.addAnimation(animation);

		return animationSet;
	}

	public Animation getInsertAnimation(View view, long startTime) {
		AnimationSet animationSet = new AnimationSet(true);
		animationSet.setStartOffset(startTime);
		animationSet.setDuration(ANIMATION_DURATION);

		Animation animation = new ScaleAnimation(1, 1, 0, 1, 0, view.getHeight() / 2);
		animation.setDuration(ANIMATION_DURATION);
		animationSet.addAnimation(animation);

		animation = new AlphaAnimation(0, 1);
		animation.setDuration(ANIMATION_DELAY);
		animationSet.addAnimation(animation);

		animationSet.setInterpolator(getContext(), android.R.anim.decelerate_interpolator);
		return animationSet;
	}

	public Animation getRemoveAnimation(View view, AnimationListener listener) {
		AnimationSet animationSet = new AnimationSet(true);
		animationSet.setAnimationListener(listener);
		animationSet.setDuration(ANIMATION_DURATION);

		Animation animation = new RotateAnimation(0, -35, view.getWidth(), 0);
		animation.setDuration(ANIMATION_DURATION);
		animationSet.addAnimation(animation);

		animation = new AlphaAnimation(1, 0);
		animation.setDuration(ANIMATION_DURATION);
		animationSet.addAnimation(animation);

		animation = new TranslateAnimation(0, view.getWidth() / 2, 0, 0);
		animation.setDuration(ANIMATION_DURATION);
		animation.setStartOffset(ANIMATION_DELAY);
		animationSet.addAnimation(animation);

		return animationSet;
	}

}
